package cn.tedu.sso.service.Impl;

import cn.tedu.base.response.JsonResult;
import cn.tedu.base.response.StatusCode;
import cn.tedu.base.utils.HashPasswordUtil;
import cn.tedu.base.utils.JWTUtil;
import cn.tedu.sso.controller.CustomWebSocketEndpoint;
import cn.tedu.sso.dao.mappers.LoginMapper;
import cn.tedu.sso.pojo.entity.Client;
import cn.tedu.sso.pojo.po.LoginByCodeRequest;
import cn.tedu.sso.pojo.po.LoginRequest;
import cn.tedu.sso.pojo.vo.UserLoginVO;
import cn.tedu.sso.service.SSOService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class SSOServiceImpl implements SSOService {

    String verificationEmailBody = "【智学博客】登录验证码为:";
    String tips = ",请在5分钟内输入，如非本人操作请忽略";
    String key = "ver_code_";
    String keyLogin = "ver_info_";

    List<Client> temporaryClient;
    Map<String, Object> temporaryStringLoginRequestmap;
    UserLoginVO temporaryUserLongVo;
    String temporaryKey;
    String temporaryValue;
    String temporaryLastId;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private LoginMapper loginMapper;

    @Qualifier("redisTemplate")
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public JsonResult login(LoginRequest loginRequest, HttpServletRequest request) {
        log.debug("用户登录信息二次验证：loginRequest:{}", loginRequest);
        UserLoginVO userLoginVO = new UserLoginVO();
        //创建一个用于生成token所需要的存储数据的HashMap
        Map<String, Object> stringLoginRequestmap = new HashMap<>();
        //定义token
        String visitToken;
        String refreshToken;
        // 1、获取前端请求的ip地址
        String remoteAddr = request.getRemoteAddr();
        log.debug("获取的前端请求的ip地址remoteAddr:{}",remoteAddr);
        // 2、获取前端请求的设备信息
        String userAgent = request.getHeader("User-Agent");
        log.debug("获取的前端请求的标识客户端的类型、版本和操作系统等相关信息userAgent:{}",userAgent);
        // 3、拼接key与value
        String key = keyLogin + loginRequest.getPhoneNum();
        log.debug("key:{}",key);
        String value = userAgent + " ip/" + remoteAddr;
        // 4、通过拼接的key去redis中查询是否有对应的key的值
        Object object = stringRedisTemplate.opsForValue().get(key);
        System.out.println(object);
        if (object == null){
            // 4.1 说明没有这个键，说明之前没有登录过，进行密码校验
            if (StringUtils.isEmpty(loginRequest.getPhoneNum()) || StringUtils.isEmpty(loginRequest.getPassword())) {
                //说明用户没有输入
                return new JsonResult(StatusCode.USER_NOT_EXIST);
            } else {
                //根据用户输入的信息与数据库中信息进行对比，查看是否有这个人
                //创建查询条件
                QueryWrapper<Client> clientQueryWrapper = new QueryWrapper<>();
                //设置查询条件手机号相同且密码相同
                clientQueryWrapper.select("id", "phone_num", "password")
                        .eq("phone_num", loginRequest.getPhoneNum());
                List<Client> clients = loginMapper.selectList(clientQueryWrapper);
                if (!clients.isEmpty()) {
                    //此时说明用户存在
                    String hPassword = HashPasswordUtil.hashPassword(loginRequest.getPassword());
                    QueryWrapper<Client> nextClientQueryWrapper = new QueryWrapper<>();
                    //设置查询条件名字相同且密码相同
                    nextClientQueryWrapper.select("id", "phone_num", "password")
                            .eq("phone_num", loginRequest.getPhoneNum())
                            .eq("password", hPassword);
                    List<Client> nextClients = loginMapper.selectList(nextClientQueryWrapper);
                    log.debug("nextClients:{}", nextClients);
                    if (!nextClients.isEmpty()) {
                        //说明用户输入的用户名/密码正确
                        //获取用户对象
                        return getResult(nextClients,stringLoginRequestmap,userLoginVO,key,value);
                    } else {
                        //此时说明用户存在但密码错误
                        return new JsonResult(StatusCode.USERNAME_PASSWORD_ERROR);
                    }
                } else {
                    log.debug("此用户不存在");
                    return new JsonResult(StatusCode.USER_NOT_EXIST);
                }
            }
        } else if (object.equals(value)){
            // 5.1 说明当前设备上已经登录过了，所以无需再登录;
            log.debug("当前本地已登录");
            return new JsonResult(StatusCode.LOGIN_EXISTS);
        } else {
            //TODO 踢下线的业务
            // CustomWebSocketEndpoint.sendMessageToUser(String userId,"您的账号在其他设备登录，如果非本人操作，请尽快修改密码，避免账号丢失")
         // 5.2 说明其他设备上登录过了
            // 5.2.1 因为已经确定之前有人或其他设备登陆过，所以账户肯定是存在的，只需进行密码校验
            QueryWrapper<Client> clientQueryWrapper = new QueryWrapper<>();
            String hPassword = HashPasswordUtil.hashPassword(loginRequest.getPassword());
            clientQueryWrapper.select("id","phone_num","password")
                    .eq("phone_num",loginRequest.getPhoneNum())
                    .eq("password",hPassword);
            List<Client> clients = loginMapper.selectList(clientQueryWrapper);

            if (clients.isEmpty()){
                //说明密码错误
                return new JsonResult(StatusCode.USERNAME_PASSWORD_ERROR);
            } else {
                //说明用户输入的用户名/密码正确
                //返回给用户一个消息，提示他 当前账户已其他设备登录，是否继续登录
                temporaryClient = clients;
                temporaryStringLoginRequestmap = stringLoginRequestmap;
                temporaryUserLongVo = userLoginVO;
                temporaryKey = key;
                temporaryValue = value;
                //将另一设备登录的id拿出来，赋值给临时变量存储
                temporaryLastId = clients.get(0).getId().toString();
                return new JsonResult(StatusCode.LOGIN_CONTINUE);
            }
        }
    }

    @Override
    public void verificationCode(String phoneNum) {
        //通过getVerificationCode生成随机的4位验证码
        int verificationCode = getVerificationCode(phoneNum);
        String verificationNote = verificationEmailBody + verificationCode + tips;
        HttpClient client = new HttpClient();
        PostMethod post = new PostMethod("http://sms.webchinese.cn/web_api/");
        post.addRequestHeader("Content-Type",
                "application/x-www-form-urlencoded;charset=gbk");//在头文件中设置转码
        NameValuePair[] data = { new NameValuePair("Uid", "FLAMEKLS"),//注册的用户名
                new NameValuePair("Key", "98DD24F52D41C64F905B2752EA81008F"),//注册成功后,登录网站使用的密钥
                new NameValuePair("smsMob", "13550651839"),//手机号码
                new NameValuePair("smsText", verificationNote)};//设置短信内容
        post.setRequestBody(data);
        try {
            client.executeMethod(post);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        Header[] headers = post.getResponseHeaders();
        int statusCode=post.getStatusCode();
        System.out.println("statusCode:"+statusCode);
        for (Header header : headers) {
            System.out.println(header.toString());
        }
        String result = null;
        try {
            result = new String(post.getResponseBodyAsString().getBytes("gbk"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        System.out.println(result);
        post.releaseConnection();
    }

    @Override
    public JsonResult loginByCode(LoginByCodeRequest loginByCodeRequest) {
        Map<String, Object> stringLoginRequestmap = new HashMap<>();
        UserLoginVO userLoginVO = new UserLoginVO();
        String visitToken;
        String refreshToken;
        log.debug("二次loginRequest:{}",loginByCodeRequest);
        String phoneNum = loginByCodeRequest.getPhoneNum();
        QueryWrapper<Client> clientQueryWrapper = new QueryWrapper<>();
        clientQueryWrapper.select("id","phone_num","password")
                .eq("phone_num",phoneNum);
        List<Client> clients = loginMapper.selectList(clientQueryWrapper);
        if (!clients.isEmpty()){
            //此时说明该用户是注册了的
            String keyByPhone = key + phoneNum;
            String code = stringRedisTemplate.opsForValue().get(keyByPhone);
            if (loginByCodeRequest.getVerificationCode().equals(code)) {
                //说明输入的code是正确的
                Client client = clients.get(0);
                String jsonString = JSON.toJSONString(client);
                //将查询出来的client用于之后的生成token
                stringLoginRequestmap.put("userInfo", jsonString);
                //生成访问token和刷新token并返回
                visitToken = JWTUtil.getVisitToken(stringLoginRequestmap);
                refreshToken = JWTUtil.getRefreshToken(stringLoginRequestmap);
                userLoginVO.setId(client.getId());
                userLoginVO.setVisitToken(visitToken);
                userLoginVO.setRefreshToken(refreshToken);
                return JsonResult.loginOk(userLoginVO);
            } else {
                //说明输入的code是错误的
                return new JsonResult(StatusCode.CODE_ERROR);
            }
        } else {
            return new JsonResult(StatusCode.USER_NOT_EXIST);
        }
    }

    @Override
    public JsonResult loginContinue() throws IOException {
        //执行这个方法说明用户输入的手机号和密码/验证码都是正确的,并且用户点击了继续登录
        //1、发送信息给另一个设备登录的那个b，告诉它被踢下线了，调用CustomWebSocketEndpoint的sendMessageToUser方法
        log.debug("执行发送消息");
        HashMap<String, String> map = new HashMap<>();
        map.put("提示","您的账号在其他设备登录，如果非本人操作，请尽快修改密码，避免账号丢失");
        CustomWebSocketEndpoint.sendMessageToUser(temporaryLastId,JSON.toJSONString(map));
        //2、发送完消息后，生成新的值以及新的JsonResult返回给前端
        return getResult(temporaryClient,temporaryStringLoginRequestmap,temporaryUserLongVo,temporaryKey,temporaryValue);
    }

    @Override
    public JsonResult outLogin(Long userId) {
        //通过userId查询到phoneNum;
        QueryWrapper<Client> clientQueryWrapper = new QueryWrapper<>();
        clientQueryWrapper.select("phone_num")
                .eq("id",userId);
        Client client = loginMapper.selectOne(clientQueryWrapper);
        Boolean delete = stringRedisTemplate.delete(keyLogin + client.getPhoneNum());
        if (delete){
            return new JsonResult(StatusCode.OPERATION_SUCCESS);
        } else {
            return new JsonResult(StatusCode.OPERATION_FAILED);
        }
    }

    public int getVerificationCode(String phoneNum) {
        SecureRandom secureRandom = new SecureRandom();
        int verificationCode = 1000 + secureRandom.nextInt(9000); // 生成范围在1000到9999之间的数
        System.out.println("生成的验证码是: " + verificationCode);
        String keyByPhone = key + phoneNum;
        stringRedisTemplate.opsForValue().set(keyByPhone, String.valueOf(verificationCode), 1, TimeUnit.DAYS);
        return verificationCode;
    }

    private @NotNull JsonResult getResult(List<Client> clients, Map<String, Object> stringLoginRequestmap, UserLoginVO userLoginVO, String key, String value) {
        log.debug("发送消息执行完毕，执行登录");
        String visitToken;
        String refreshToken;
        Client client = clients.get(0);
        String jsonString = JSON.toJSONString(client);
        //将查询出来的client用于之后的生成token
        stringLoginRequestmap.put("userInfo", jsonString);
        //生成访问token和刷新token并返回
        visitToken = JWTUtil.getVisitToken(stringLoginRequestmap);
        refreshToken = JWTUtil.getRefreshToken(stringLoginRequestmap);
        //将获取的client对象的值赋值给userLoginVO对象
        BeanUtils.copyProperties(client, userLoginVO);
        userLoginVO.setId(client.getId());
        userLoginVO.setVisitToken(visitToken);
        userLoginVO.setRefreshToken(refreshToken);
        userLoginVO.setLoginStatus("登录成功");
        // 4.2 将拼接后得到的value，作为键值对的值
        stringRedisTemplate.opsForValue().set(key, value,7,TimeUnit.DAYS);
        // 4.2 返回登录结果
        log.debug("登录成功userLoginVO:{}", userLoginVO);
        return JsonResult.loginOk(userLoginVO);
    }
}
